Function.RegisterNamespace("System.Script.Strategy");

System.Script.Strategy.StrategyManager=function(){
    var _strategies;
    var _strategy;
    var _candidate;
    
    // ctor
    function StrategyManager(strategy1,strategy2,strategyN){
        _strategies=[];
        Array.ForEach(Array.prototype.slice.call(arguments,0),addStrategy,strategyPredicate);
    }
    StrategyManager.apply(this,arguments);
    
    this.Add=function(strategy,index){
        if(!isStrategy(strategy))throw new Error("System.Script.Strategy.StrategyManager.Add: 'strategy' must implement System.Script.Strategy.IStrategySpecification");
        addStrategy(strategy,null,index);
        clearCache();
    };
    
    this.Clear=function(){
        _strategies=[];
        clearCache();
    };
    
    this.Get=function(candidate,isVolatile,properties){
        if(isVolatile||!_strategy||_candidate!=candidate){
            var context={
                Candidate:candidate,
                Strategy:null
            };
            Array.ForEach(_strategies,getStrategy,null,context);
            if(context.Strategy){
                _candidate=candidate;
                _strategy=context.Strategy;
            }
        }
        if(!_strategy)throw new Error("System.Script.Strategy.StrategyManager.Get: No viable strategy found.");
        Object.Copy(_strategy,properties);
        return _strategy;
    };
    
    this.Enumerate=function(){
        return _strategies.slice(0);
    };
    
    this.Remove=function(strategy){
        if(!isStrategy(strategy))throw new Error("System.Script.Strategy.StrategyManager.Remove: 'strategy' must implement System.Script.Strategy.IStrategySpecification");
        Array.Remove(_strategies,strategy);
        clearCache();
    };
    
    // Private methods
    function addStrategy(strategy,context,index){
        if(index==null)index=_strategies.length;
        _strategies.splice(index,null,strategy);
    }
    
    function clearCache(){
        _strategy=null;
        _candidate=null;
    }

    function getStrategy(strategy,context){
        try{
            var instance=new strategy();
            if(instance.IsSatisfiedBy(context.Candidate)){
                context.Strategy=instance;
                context.Cancel=true;
            }
        }catch(e){
            throw new Error(String.Format("System.IO.File.getStrategy: unable to instantiate strategy '{0}'. Check your constructor logic for dependencies. Error: {1}",Function.GetName(_strategies[context.Index]),e));
        }
    }
    
    function isStrategy(strategy){
        return Object.Implements(System.Script.Strategy.IStrategySpecification,strategy);
    }

    // Predicates
    function strategyPredicate(strategy,context){
        if(!isStrategy(strategy))throw new Error(String.Format("System.Script.Strategy.StrategyManager.ctor: 'strategy' at position [{0}] does not implement IStrategySpecification.",context.Index));
        return true;
    }
};